import json
import os.path
import struct
import datetime
import threading
import time
import requests
import redis
class TIotTer:
    def __init__(self):
        self.terCode=""
        self.settings= {
            "apiUrl":"http://192.168.1.11:12001/znapi",
            "dbCode":"zniot2025",
            "tdbCode":"zniot2025",
        }
        self.terData={
            "terInfo":{},
            "buses":{},
            "devs": {},
            "tasks": {},
            "vars": {}
        }
        self.busClasses={}
        self.buses={}
        self.redisPool = None
        self.redisClient = None
    def loadTerData(self):
        try:
            url = self.apiUrl+"/db/query"
            ps ={
                "code": self.settings['dbCode'],
                "key":"ter/getTerDatas",
                "params":{
                    "terCode" : self.terCode
                }
            }
            resp = requests.post(  url , json= ps)
            res = resp.json(encoding='UTF-8')
            self.terData['terInfo'] = res['datasetList'][0][0]

            print('redis json:' , self.terData['terInfo']['redisParam'])
            ps = json.loads(self.terData['terInfo']['redisParam'])
            self.terData['terInfo']['redisPs']  = ps
            print('redis' , ps)
            for i in res['datasetList'][1]:
                self.terData['buses'][ i['busCode']] = i
            for i in res['datasetList'][2]:
                self.terData['devs'][ i['devCode']] = i
            for i in res['datasetList'][3]:
                self.terData['tasks'][ i['taskCode']] = i
            for i in res['datasetList'][4]:
                self.terData['vars'][ i['varId']] = i
            r = redis.StrictRedis(**ps)
            if  r.ping() :
                self.redisClient = r
                print('connect redis success.')
            else:
                print('connect redis faild.')
            res= None
            resp = None
        except Exception as er:
            print('loadTerData',er)
    def startBuses(self ):
        try:
            for busCode in self.terData['buses'].keys():
                bus = self.getBus(busCode)
                if bus !=None:
                    bus.restart()
        except Exception as er:
            print(er)
    def registBusType(self, className , busClass):
        try:
            self.busClasses[className]= busClass
        except Exception as er:
            print(er)
    def getBus(self, busCode):
        res = None
        try:
            if busCode in self.buses.keys():
                res = self.buses[busCode]
            elif busCode in self.terData['buses'].keys()  :
                busType = self.terData['buses'][busCode]['busType']
                if busType in self.busClasses.keys():
                    bus = self.busClasses[busType](self)
                    bus.busCode = busCode
                    self.buses[busCode] = bus
                    res = bus
        except Exception as er:
            print(er)
        return  res
    def writeRedisHash(self , dataPath , dictData):
        try:
            if self.redisClient !=None:
                self.redisClient.hmset(dataPath , dictData)
        except Exception as er:
            print('&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&')
            print('write redis error.',er)
            print('key:' , dataPath)
            print('data:' , dictData)
            print('&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&')
class TIotVarValue :
    def __init__(self):
        self.var={}
        self.v0 = 0
        self.tm0 = 0
        self.v1 = 0
        self.tm1 = 0
        self.disp = ""
        self.changed = False

    def decode(self , registerData):
        v = 0
        vd =''
        try:
            dataType = self.var['dataType']
            regLen = self.var['regLen']
            bitFrom= self.var['bitFrom']
            bitLen = self.var['bitLen']
            decSize= self.var['decSize']
            rate1  = self.var['rate1']
            rate2  = self.var['rate2']
            regBytes = None
            try:
                regBytes = bytearray(list(registerData))
            except Exception as erre:
                pass
            vsIndex = 0
            if dataType.lower() == 'bitield16' :
                mask = 1>>(bitFrom % 8)
                vsIndex = 0 if bitFrom>7 else 1
                v = regBytes[vsIndex] & mask
            elif dataType.lower()=='i16':
                v = (struct.unpack(">h" , regBytes))[0]
            elif dataType.lower()=='u16':
                v = (struct.unpack(">H" , regBytes))[0]
            elif dataType.lower()=='i32':
                v = (struct.unpack(">i" , regBytes))[0]
            elif dataType.lower()=='u32':
                v = (struct.unpack(">I" , regBytes))[0]
            elif dataType.lower()=='float':
                v = (struct.unpack(">f" , regBytes))[0]
            elif dataType.lower()=='str':
                v = 0
                vd = regBytes.decode('ascii')
            elif dataType.lower()=='mld':
                v = 0
                vd = ",".join([str(x) for x in regBytes ])
            elif dataType.lower()=='um':
                bytes = regBytes[2*bitFrom:2*(bitFrom+bitLen)]
                f = ">H" if bitLen==1 else ">I"
                v = (struct.unpack(f , bytes))[0]
                vd = str(v)
            elif dataType.lower()=='array':
                v = registerData[bitFrom]
                vd = str(v)
            elif dataType.lower()=='u32time':
                v = regBytes[0]
                dt =  datetime.datetime.fromtimestamp(v)
                vd = datetime.datetime.strftime(dt,'%Y-%m-%d %H:%M:%S')
            elif dataType.lower()=='u645':
                #v = int.from_bytes(  regBytes, byteorder='big')
                v = registerData[0]
                vd = str(v)

            if rate1!=1 :
                v = v * rate1
            if rate2!=1 :
                v = v * rate2

            '''
            if decSize==1:
                v = 0.1*int(v * 10)
            elif decSize==2:
                v = 0.01*int(v * 100)
            elif decSize==3:
                v = 0.001*int(v * 1000)
            elif decSize==4:
                v = 0.0001*int(v * 10000)
            elif decSize==5:
                v = 0.00001*int(v * 100000)
            '''
            #int.from_bytes(struct.unpack(float('f',a)) , byteorder='little')
            if decSize>0 :
                l1 = len(str(v))
                l2 = len(str(int(v)))
                if (l2+1+decSize) > l1 :
                    v = float(str(v)[0:l2+1+decSize])

        except Exception as er:
            print('decode error' , er)
        return  v, vd
    def setVarValue(self , registerData):
        try:
            v, vd = self.decode(registerData)
            self.v1 = v
            self.tm1 = time.time()
            self.disp = vd
            changed = self.changed
            if self.tm0 ==0 :
                changed = True
            elif self.tm1 - self.tm0 > 3600:
                changed = True
            elif (self.var['varType']=='param' or self.var['varType']=='status' or self.var['varType']=='warn' ) and (self.v1!=self.v0):
                changed = True
            elif self.var['varType'] =='value' and self.v1!=self.v0:
                if self.v0 == 0 :
                    changed = True
                else:
                    rate = abs((self.v1 - self.v0)/self.v0)
                    if rate>= self.var['valueThreshold']:
                        changed = True
            self.changed = changed
        except Exception as er:
            print('decode error' , er)
    def getJson(self):
        res ={}
        try:
            res={
                'id': self.var['varId'],
                't': self.tm1 ,
                "v": self.v1,
                "d": self.disp
            }
        except Exception as er:
            print(er)
        return  res
    def getTDBContent(self):
        res =""
        try:
            v = str( self.v1)
            if len(v)>12 :
                try:
                    pos = v.find(".")
                    if pos>0:
                        v = v[:pos+1+ self.var['decSize']]
                except Exception as eef:
                    pass
            res = f"{self.var['devCode']},vid={self.var['varId']} v={v}"
        except Exception as er:
            print(er)
        return  res
class TIotBus:
    def __init__(self , ter ):
        self.busCode="";
        self.ter : TIotTer = ter
        self.tasks={}
        self.vars={}
        self.values={}
        self.running = False
        self.runThread = None
    def init (self):
        try:
            self.busInfo= self.ter.terData['buses'][self.busCode]
            self.devs= {}
            self.tasks= {}
            self.vars = {}
            self.values={}
            for i in self.ter.terData['devs'].values():
                if i['busCode'] == self.busCode:
                    self.devs[i["devCode"]] = i
            for i in self.ter.terData['tasks'].values():
                if i['busCode'] == self.busCode:
                    i['lastTm'] = 0
                    self.tasks[i["taskCode"]] = i
                    i['varValues']=[]
            for i in self.ter.terData['vars'].values():
                if i['busCode'] == self.busCode:
                    self.vars[i["varId"]] = i
                    varValue = TIotVarValue()
                    varValue.var = i
                    self.values[i["varId"]] = varValue
                    taskCode = i['taskCode']
                    if taskCode in self.tasks.keys():
                        self.tasks[taskCode]['varValues'].append(varValue)
        except Exception as er:
            print(er)
    def start (self):
        try:
            pass
        except Exception as er:
            print(er)
    def stop (self):
        try:
            pass
        except Exception as er:
            print(er)
    def restart (self):
        try:
            self.stop()
            self.init()
            self.runThread = threading.Thread( target= self.start , daemon= True)
            self.running = True
            self.runThread.start()
        except Exception as er:
            print(er)
    def saveVarvalues (self , task , varValues):
        try:
            dirName = os.path.join(self.ter.terData['terInfo']['dataPath'] , 'local' , task['devCode'])
            fn = datetime.datetime.now().strftime("%Y%m%d")+".txt"
            if not os.path.exists(dirName):
                os.makedirs(dirName,0x777)
            saveFn = os.path.join(dirName , fn)
            content = "\n".join([json.dumps(  x.getJson() ) for x in varValues])
            with open(saveFn , 'a' , encoding='utf-8') as f:
                f.write(content+'\n')
        except Exception as er:
            print(er)
    def uploadVarvalues (self , task, varValues):
        try:
            url = self.ter.terData['terInfo']['tdbUrl']
            db = self.ter.terData['terInfo']['tdbDB']
            token = self.ter.terData['terInfo']['tdbToken']
            repolicy = self.ter.terData['terInfo']['tdbRePolicy']
            content = "\n".join([x.getTDBContent() for x in varValues])
            headers = {
                "Authorization": token,
                'Content-Type': 'application/json',
            }
            influxdb_url = f"{url}/write?db={db}"
            if repolicy != '':
                influxdb_url = influxdb_url + '&rp='+ repolicy
            resp = requests.post(url=influxdb_url, headers=headers, data=content)

            if str(resp.status_code) == '204' or str(resp.status_code) == '200':
                pass
            else:
                print(resp.content)
        except Exception as er:
            print(er)
    def updateServerVar (self , task, varValues):
        try:
            dictKvs ={}
            for item in varValues:
                dictKvs[item.var['varId']] =item.getJson()
            url = self.ter.apiUrl+"/io/setBatch"
            ps ={
                "bucketCode":"bkKvIot2025",
                "dictKeyValue": dictKvs
            }
            resp = requests.post(  url , json= ps)
            resp = None

        except Exception as er:
            print(er)
    def updateRegisRealValue(self,varValues):
        res = None
        try:
            dictValues ={}
            dictTimes ={}
            if len(varValues)>0:
                nowTm = time.time()
                for item in varValues:
                    devKey = f"zniot2025/realvalue/{item.var['sysCode']}/{item.var['devCode']}"
                    devTimeKey = f"zniot2025/realtime/{item.var['sysCode']}/{item.var['devCode']}"
                    print(f'---------->>> {item.var}, {item}')
                    if not devKey in dictValues.keys():
                        dictValues[devKey]={}
                    if not devTimeKey in dictTimes.keys():
                        dictTimes[devTimeKey]={}
                    vId = str(item.var['varId'])
                    dictValues[devKey][vId] = item.v1
                    dictTimes[devTimeKey][vId] = nowTm
                for k , v in dictValues.items():
                    self.ter.writeRedisHash(k, v)
                for k , v in dictTimes.items():
                    self.ter.writeRedisHash(k, v)
        except Exception as er:
            print(er)
        return  res
    def doOnTask(self , task , registerData):
        try:
            print('send 1 redis')
            varValues =[]
            for valueItem in task['varValues']:
                valueItem.setVarValue(registerData)
                if valueItem.changed :
                    varValues.append(valueItem)
            if len(varValues)>0:
                print('send 2 redis')
                self.saveVarvalues(task ,varValues)
                self.uploadVarvalues(task , varValues)
                self.updateServerVar(task , varValues )
                self.updateRegisRealValue(varValues)
                for item in varValues:
                    item.v0 = item.v1
                    item.tm0 = item.tm1
                    item.changed = False
        except Exception as er:
            print('decode error' , er)


iotTer = TIotTer( )
